const isIndexedDBValid = idb=> {
  try {
    if (!idb || !idb.open) {
      return false;
    }
    const isSafari =
      typeof openDatabase !== 'undefined' &&
      /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) &&
      !/Chrome/.test(navigator.userAgent) &&
      !/BlackBerry/.test(navigator.platform);

    const hasFetch =
      typeof fetch === 'function' &&
      fetch.toString().indexOf('[native code') !== -1;

    return (
      (!isSafari || hasFetch) &&
      typeof indexedDB !== 'undefined' &&
      typeof IDBKeyRange !== 'undefined'
    );
  } catch (e) {
    return false;
  }
};
class DriverStorage {
  constructor({ name, storeName, isOnlyDB } = { name: 'localforage', storeName: 'keyvaluepairs', isOnlyDB: true }) {
    this.name = name;
    this.storeName = storeName;

    // 是否默认只使用indexedDB,否则的话indexedDB不支持的时候使用localstorage
    this.isOnlyDB = isOnlyDB;
    this.indexdb = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB || '';
    this.noDBError = '数据库不存在或没有初始化';
    this.supportIndexedDB = true;
    this.dbPromise = this.initStoragePromise();
  }

  initStoragePromise() {
    return new Promise((resolve, reject) => {
      if (this.indexdb && !isIndexedDBValid(this.indexdb) && !this.isOnlyDB) {
        resolve({
          db: localStorage,
          supportIndexedDB: false,
        });
      }
      const version = 3; // 用2会报错，不知道什么原因
      const openRequest = this.indexdb.open(this.name, version);
      openRequest.onupgradeneeded = e=> {
        const db = e?.target?.result;
        try {
          if (db && !db.objectStoreNames.contains(this.storeName)) {
            db.createObjectStore(this.storeName);
          }
        } catch (error) {
          throw Error('创建indexedDB表失败,请检查');
        }
      };
      openRequest.onsuccess = e=> {
        resolve({
          db: e?.target?.result,
          supportIndexedDB: true,
        });
      };
      openRequest.onerror = function(e) {
        e.preventDefault();
        resolve({
          db: null,
          supportIndexedDB: true,
        });
      };
    });
  }

  getStore(db, type = 'readwrite') {
    if (db) {
      const transaction = db.transaction([this.storeName], type);
      const store = transaction.objectStore(this.storeName);
      return store;
    } else {
      return null;
    }
  }

  async setItem(k, v) {
    const allKeys = await this.keys() || [];
    const hasKey = allKeys.includes(k);
    let request = null;
    return new Promise((resolve, reject)=>{
      this.dbPromise.then(res=>{
        const { supportIndexedDB, db } = res;
        if (supportIndexedDB) {
          const store = this.getStore(db);
          if (!store) {
            reject(new Error(this.noDBError));
          }
          if (hasKey) {
            request = store.put(v, k);
          } else {
            request = store.add(v, k);
          }

          request.onsuccess = ()=>{
            resolve();
          };
          request.onerror = e=>{
            reject(e);
          };
        } else {
          try {
            const key = `${this.name}_${this.storeName}/${k}`;
            const value = (typeof v !== 'string') ? JSON.stringify(v) : v;
            db.setItem(key, value);
            resolve();
          } catch (e) {
            reject(e);
          }
        }
      });
    });
  }

  getItem(k) {
    return new Promise((resolve, reject)=>{
      this.dbPromise.then(res=>{
        const { supportIndexedDB, db } = res;
        if (supportIndexedDB) {
          const store = this.getStore(db, 'readonly');
          if (!store) {
            reject(new Error(this.noDBError));
          }
          const request = store.get(k);
          request.onsuccess = function( event) {
            if (request.result) {
              resolve(request.result);
            } else {
              resolve('');
            }
          };
          request.onerror = e=>{
            reject(new Error('获取失败'));
          };
        } else {
          try {
            const key = `${this.name}_${this.storeName}/${k}`;
            const value = JSON.parse(db.getItem(key));
            resolve(value);
          } catch (error) {
            reject(new Error('获取失败'));
          }
        }
      });
    });
  }

  removeItem(key) {
    return new Promise((resolve, reject)=>{
      this.dbPromise.then(res=>{
        const { supportIndexedDB, db } = res;
        if (supportIndexedDB) {
          const store = this.getStore(db);
          if (!store) {
            reject(new Error(this.noDBError));
          }
          const request = store.delete(key);
          request.onsuccess = ()=>{
            resolve();
          };
          request.onerror = e=>{
            reject(e);
            throw Error(e);
          };
        } else {
          try {
            const k = `${this.name}_${this.storeName}/${key}`;
            db.removeItem(k);
          } catch (error) {
            reject(error);
          }
        }
      });
    });
  }

  clear() {
    return new Promise((resolve, reject)=>{
      this.dbPromise.then(res=>{
        const { supportIndexedDB, db } = res;
        if (supportIndexedDB) {
          const store = this.getStore(db);
          if (!store) {
            reject(new Error(this.noDBError));
          }
          const request = store.clear();
          request.onsuccess = ()=>{
            resolve();
          };
          reject.onsuccess = e=>{
            reject(e);
          };
        } else {
          try {
            db.clear();
            resolve();
          } catch (error) {
            reject(error);
          }
        }
      });
    });
  }

  keys() {
    return new Promise((resolve, reject)=>{
      this.dbPromise.then(res=>{
        const { supportIndexedDB, db } = res;
        if (supportIndexedDB) {
          const store = this.getStore(db, 'readonly');
          if (!store) {
            reject(new Error(this.noDBError));
          }
          const request = store.openCursor();
          const keysList = [];
          request.onsuccess = function(e) {
            const cursor = e.target.result;
            if (cursor && cursor !== null) {
              const problem = cursor.key;
              keysList.push(problem);
              cursor.continue();
            } else {
              resolve(keysList);
            }
          };
          request.onerror = e=>{
            reject(e);
          };
        } else {
          try {
            const lKey = `${this.name}_${this.storeName}/`;
            const keys = Object.keys(db).map(kkk=>{
              if (kkk.startsWith(lKey)) {
                return kkk.replace(lKey, '');
              } else {
                return '';
              }
            }).filter(kk=>kk);
            resolve(keys);
          } catch (error) {
            reject(error);
          }
        }
      });
    });
  }
}
export default DriverStorage;
